﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Net;

//
using System.ServiceModel.Web;
using System.ServiceModel.Activation;
using System.Web.Hosting;


namespace BecktonService
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Service1 : IService1
    {
        #region ATTRIBUTES
        
        int userId = -1;
        bool isAdmin = false;
        static string livemeshFolder = @"C:\Users\" + Environment.UserName + @"\livemesh";
        MeshDataObjectsDataContext db = new MeshDataObjectsDataContext(livemeshFolder + @"\base.mdf");
        
        #endregion
        
        #region SERVICE_METHODS
        public Service1()
        {
            db.Log = Console.Out;
        }
        
        private void createAdminIfNoExist()
        { 
            this.register("admin@admin.com", "admin");
        }

        public bool connect(string email, string password)
        {
            var qUserExists = from users in db.Users 
                              where users.email == email 
                              where users.password == password
                              select users.userID;
            if (qUserExists.Count() != 1)
                return false;
            this.userId = qUserExists.First();
            return true;
        }

        private int getUserId(string email)
        {
            var qUser = from users in db.Users where users.email == email select users.userID;
            if (qUser.Count() > 0)
                return qUser.First();
            else
                return -1;
        }

        private int getUserParentDirectoryId(int id)
        {
            var qParentDirectory = from dir in db.Directories
                                   where dir.userID == id && dir.parentDirectoryID == -1
                                   select dir.directoryID;
            if (qParentDirectory.Count() > 0)
                return qParentDirectory.First();
            else
                return -1;
        }

        public bool register(string email, string password)
        {
            var qUserExists = from users in db.Users where users.email == email select users.userID;
            if (qUserExists.Count() > 0 || String.IsNullOrEmpty(email) || String.IsNullOrEmpty(password))
                return false;
               User user = new User
                {
                    email = email,
                    password = password
                };
                
                try
                {
                    db.Users.InsertOnSubmit(user);
                    db.SubmitChanges();
                }
                catch (Exception)
                { return false; }
                this.userId = user.userID;
                this.addDirectory("/", -1);
                this.userId = -1;
            return true;
        }

        #endregion

        #region FRIEND

        public bool addFriend(string email)
        {
            if (this.userId == -1)
                return false;

            int friendId = this.getUserId(email);
            if (friendId == -1 || friendId == this.userId)
                return false;
            
            var qFriendsExists = from friends in db.Friends
                                where ((friends.ui1 == this.userId || friends.ui1 == friendId) && 
                                (friends.ui2 == this.userId || friends.ui2 == friendId))
                                select friends;
            if (qFriendsExists.Count() != 0)
                return false;

            Friend friend = new Friend
            {
                ui1 = this.userId,
                ui2 = friendId
            };

            try
            {
                db.Friends.InsertOnSubmit(friend);
                db.SubmitChanges();
            }
            catch (Exception)
            { return false; }
            return true;
        }

        public void removeFriend(string email)
        {
            if (this.userId == -1)
                return;

            int friendId = this.getUserId(email);
            if (friendId == -1 || friendId == this.userId)
                return;

            var qFriendsExists = from friends in db.Friends
                                 where ((friends.ui1 == this.userId || friends.ui1 == friendId) &&
                                 (friends.ui2 == this.userId || friends.ui2 == friendId))
                                 select friends;
            if (qFriendsExists.Count() != 0)
            {
                try
                {
                    db.Friends.DeleteOnSubmit(qFriendsExists.First());
                    db.SubmitChanges();
                }
                catch (Exception)
                { }
            }
        }

        public List<string> getFriends()
        {
            if (this.userId == -1)
                return new List<string>();

            var qFriends = from users in db.Users
                join friends in db.Friends
                     on users.userID equals ((friends.ui1 == this.userId) ? friends.ui2 : friends.ui1)            
                    where ((friends.ui1 == this.userId || friends.ui2 == this.userId))
                                 select users.email;
            return qFriends.ToList();
        }

        #endregion

        #region DIRECTORY

        private void unShareAllChildren(int directory, int friend)
        {
            var qDirectories = from directories in db.Directories
                               where directories.parentDirectoryID == directory
                               select directories.directoryID;

            foreach (int dirId in qDirectories)
            {
                var qReadDirectories = from rdir in db.ReadDirectories
                                       where rdir.directoryID == dirId && rdir.userID == friend
                                       select rdir;

                if (qReadDirectories.Count() > 0)
                {
                    try
                    {
                        db.ReadDirectories.DeleteOnSubmit(qReadDirectories.First());
                        db.SubmitChanges();
                    }
                    catch (Exception)
                    { }
                }
                this.unShareAllChildren(dirId, friend);
            }

        }

        private bool checkIfParentIsShared(int directory, int friend)
        {
            var qParentDirectory = from dir in db.Directories
                                   where dir.directoryID == directory
                                   select dir.parentDirectoryID;
           
            if (qParentDirectory.Count() == 0) // never happens
            return false;
            
            int parentDirId = qParentDirectory.First();

            if (parentDirId == -1)
                return false; // in case of root dir

            var qReadDirectories = from rdir in db.ReadDirectories
                               where rdir.directoryID == parentDirId && rdir.userID == friend
                               select rdir;
            
            if (qReadDirectories.Count() > 0)
                return true;
            return (checkIfParentIsShared(parentDirId, friend));
        }

        public int addDirectory(string dname, int parent)
        {
            if (this.userId == -1 || String.IsNullOrEmpty(dname))
                return -1;

            if (parent == -1)
                parent = this.getUserParentDirectoryId(this.userId);

            Directory dir = new Directory
            {
                userID = this.userId,
                parentDirectoryID = parent,
                name = dname
            };

            try
            {
                db.Directories.InsertOnSubmit(dir);
                db.SubmitChanges();
            }
            catch (Exception e)
            { 
                return -1; 
            }
            return dir.directoryID;
        }

        public void removeDirectory(int parent = -1)
        {

        }

        public void shareDirectory(int directory, string friendEmail)
        {
            int friendId = this.getUserId(friendEmail);

            if (friendId == -1)
                return;
            var qrDirectoryExists = from rdirectories in db.ReadDirectories
                                    where rdirectories.directoryID == directory && rdirectories.userID == friendId
                                    select rdirectories;

            if (qrDirectoryExists.Count() != 0)
                return;

            var qDirectoryOwner = from directories in db.Directories
                                  where directories.directoryID == directory && directories.userID == this.userId
                                  select directories;

            if (qDirectoryOwner.Count() == 0)
                return;

            if (this.checkIfParentIsShared(directory, friendId))
                return;

            this.unShareAllChildren(directory, friendId);

            ReadDirectory rd = new ReadDirectory
            {
                directoryID = directory,
                userID = friendId
            };

            try
            {
                db.ReadDirectories.InsertOnSubmit(rd);
                db.SubmitChanges();
            }
            catch (Exception)
            {  }
            return;
        }

        public DirectoryTree getDirectoryTree()
        {
            // check user acl here
            DirectoryTree root = new DirectoryTree
            {
                Id = -1,
            };
            this.getDirectoryTreeRec(new List<DirectoryTree> { root }, this.userId);
            return root.Children.First();
        }

        private void getDirectoryTreeRec(List<DirectoryTree> root, int user)
        {
            root.ForEach(delegate(DirectoryTree node)
            {
                int level = node.Id;

                var qDirectories = from directories in db.Directories
                                   where directories.userID == ((user != -1) ? user : directories.userID) && directories.parentDirectoryID == level
                                   select directories;

                List<DirectoryTree> children = new List<DirectoryTree>();
                foreach (var subdir in qDirectories)
                {
                    children.Add(new DirectoryTree
                    {
                        Name = subdir.name,
                        Id = subdir.directoryID
                    });
                }
                node.Children = children;
                //
                var qFiles = from files in db.Files
                                   where files.directoryID == level
                                   select files;

                List<FileDesc> filesl = new List<FileDesc>();
                foreach (var f in qFiles)
                {
                    filesl.Add(new FileDesc
                    {
                        Uri = f.uri,
                        Name = f.name,
                        Id = f.fileID
                    });
                }
                node.Files = filesl;
                //
                this.getDirectoryTreeRec(node.Children, user);
            });
        }

        public DirectoryTree getFriendDirectoryTree(string friendEmail)
        {
            DirectoryTree root = new DirectoryTree
            {
                Id = -1,
                Name = "/",
                Children = new List<DirectoryTree>()
            };

            int friendId = this.getUserId(friendEmail);
            if (friendId == -1)
                return root;

            var qRDirectories = from rdir in db.ReadDirectories
                               where rdir.userID == this.userId && rdir.Directory.userID == friendId
                                select rdir;

            foreach(var dir in qRDirectories)
            {
                DirectoryTree node = new DirectoryTree
                {
                    Id = dir.directoryID,
                    Name = dir.Directory.name
                };
                this.getDirectoryTreeRec(new List<DirectoryTree> { node }, -1);
                root.Children.Add(node);
            }
            return root;
        }
       
        #endregion

        #region FILE

        public bool uploadFile(string path)
        {
        //Respon
            WebClient wc = new WebClient();

            wc.UploadFile(path, path);
            return true;
        }

        public void addFile(int directory = -1)
        {

        }

        #endregion

        #region DOWNLOADER
    
     /*   public RemoteFileInfo DownloadFile(DownloadRequest request)
        {
            RemoteFileInfo result = new RemoteFileInfo();
            try
            {
                string filePath = System.IO.Path.Combine(livemeshFolder, request.FileName);
                System.IO.FileInfo fileInfo = new System.IO.FileInfo(filePath);

                // check if exists
                if (!fileInfo.Exists)
                     throw new System.IO.FileNotFoundException("File not found",
                                                              request.FileName);

                // open stream
                System.IO.FileStream stream = new System.IO.FileStream(filePath,
                          System.IO.FileMode.Open, System.IO.FileAccess.Read);

                // return result 
                result.FileName = request.FileName;
                result.Length = fileInfo.Length;
                result.FileByteStream = stream;
            }
            catch (Exception ex)
            {

            }
            return result;
        }*/

        #endregion
    }
}
